﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;

namespace Esponce.QRCode.Content
{
	/// <summary>
	/// Builder and parser for MECARD format (aka "Phonebook registration" from the specification)
	/// </summary>
	/// <remarks>
	/// Sample:
	/// MECARD:N:Monkey,Esponce;ADR:432 Sherwood Circle,,Lake Charles,LA 70601;TEL:+15551234560;EMAIL:monkey@esponce.com;;
	/// 
	/// Specification:
	/// http://www.nttdocomo.co.jp/english/service/developer/make/content/barcode/function/application/addressbook/index.html
	/// http://www.nttdocomo.co.jp/english/service/developer/make/content/barcode/function/application/common/index.html
	/// </remarks>
	public class MecardContentFormat : ContentFormatBase<MecardContentFormat>, IContactContentFormat
	{
		#region Constructor
		public MecardContentFormat() : base()
		{
			this.PhoneNumberList = new List<string>();
			this.EmailAddressList = new List<string>();
		}
		#endregion

		#region Public properties
		/// <summary>
		/// Gets a friendly contact name.
		/// </summary>
		public string FriendlyName
		{
			get
			{
				return string.Format("{0} {1}", this.FirstName, this.LastName).Trim();
			}
		}

		/// <summary>
		/// Gets or sets a name: last name and optional first name.
		/// </summary>
		public string Name
		{
			get
			{
				return this.LastName + "," + this.FirstName;
			}
			set
			{
				if (string.IsNullOrEmpty(value))
				{
					this.FirstName = null;
					this.LastName = null;
				}
				else
				{
					string[] parts = value.Split(',');
					if (parts.Length >= 2)
					{
						this.LastName = parts[0];
						this.FirstName = parts[1];
					}
					else
					{
						this.LastName = value;
						this.FirstName = null;
					}
				}
			}
		}

		public string FirstName
		{
			get
			{
				return this.firstName;
			}
			set
			{
				if (this.firstName != value)
				{
					this.firstName = value;
					OnPropertyChanged("FirstName");
					OnPropertyChanged("Name");
				}
			}
		}
		private string firstName;

		public string LastName
		{
			get
			{
				return this.lastName;
			}
			set
			{
				if (this.lastName != value)
				{
					this.lastName = value;
					OnPropertyChanged("LastName");
					OnPropertyChanged("Name");
				}
			}
		}
		private string lastName;

		public string SoundName
		{
			get
			{
				return this.SoundLastName + "," + this.SoundFirstName;
			}
			set
			{
				if (string.IsNullOrEmpty(value))
				{
					this.SoundFirstName = null;
					this.SoundLastName = null;
				}
				else
				{
					string[] parts = value.Split(',');
					if (parts.Length >= 2)
					{
						this.SoundLastName = parts[0];
						this.SoundFirstName = parts[1];
					}
					else
					{
						this.SoundLastName = value;
						this.SoundFirstName = null;
					}
				}
			}
		}

		public string SoundFirstName
		{
			get
			{
				return this.soundFirstName;
			}
			set
			{
				if (this.soundFirstName != value)
				{
					this.soundFirstName = value;
					OnPropertyChanged("SoundFirstName");
					OnPropertyChanged("SoundName");
				}
			}
		}
		private string soundFirstName;

		public string SoundLastName
		{
			get
			{
				return this.soundLastName;
			}
			set
			{
				if (this.soundLastName != value)
				{
					this.soundLastName = value;
					OnPropertyChanged("SoundLastName");
					OnPropertyChanged("SoundName");
				}
			}
		}
		private string soundLastName;

		/// <summary>
		/// Gets or sets primary phone number. Can contain characters #,*,0-9
		/// </summary>
		public string PhoneNumber
		{
			get
			{
				return (this.PhoneNumberList != null && this.PhoneNumberList.Count > 0 ? this.PhoneNumberList[0] : null);
			}
			set
			{
				if (string.IsNullOrEmpty(value))
				{
					this.PhoneNumberList = new List<string>();
					OnPropertyChanged("PhoneNumber");
				}
				else if (this.PhoneNumberList == null || this.PhoneNumberList.Count == 0)
				{
					this.PhoneNumberList = new List<string>() { value };
					OnPropertyChanged("PhoneNumber");
				}
				else if (this.PhoneNumberList.Count > 0)
				{
					this.PhoneNumberList[0] = value;
					OnPropertyChanged("PhoneNumber");
				}
			}
		}

		public List<string> PhoneNumberList
		{
			get
			{
				return this.phoneNumberList;
			}
			set
			{
				if (this.phoneNumberList != value)
				{
					this.phoneNumberList = value;
					OnPropertyChanged("PhoneNumberList");
				}
			}
		}
		private List<string> phoneNumberList;

		/// <summary>
		/// Gets or sets videophone number. Can contain characters #,*,0-9
		/// </summary>
		public string VideoPhoneNumber
		{
			get
			{
				return this.videoPhoneNumber;
			}
			set
			{
				if (this.videoPhoneNumber != value)
				{
					this.videoPhoneNumber = value;
					OnPropertyChanged("VideoPhoneNumber");
				}
			}
		}
		private string videoPhoneNumber;

		/// <summary>
		/// Gets or sets primary e-mail address.
		/// </summary>
		public string EmailAddress
		{
			get
			{
				return (this.EmailAddressList != null && this.EmailAddressList.Count > 0 ? this.EmailAddressList[0] : null);
			}
			set
			{
				if (string.IsNullOrEmpty(value))
				{
					this.EmailAddressList = new List<string>();
					OnPropertyChanged("EmailAddress");
				}
				else if (this.EmailAddressList == null || this.EmailAddressList.Count == 0)
				{
					this.EmailAddressList = new List<string>() { value };
					OnPropertyChanged("EmailAddress");
				}
				else if (this.EmailAddressList.Count > 0)
				{
					this.EmailAddressList[0] = value;
					OnPropertyChanged("EmailAddress");
				}
			}
		}

		public List<string> EmailAddressList
		{
			get
			{
				return this.emailAddressList;
			}
			set
			{
				if (this.emailAddressList != value)
				{
					this.emailAddressList = value;
					OnPropertyChanged("EmailAddressList");
				}
			}
		}
		private List<string> emailAddressList;

		public string Note
		{
			get
			{
				return this.note;
			}
			set
			{
				if (this.note != value)
				{
					this.note = value;
					OnPropertyChanged("Note");
				}
			}
		}
		private string note;

		public string Birthday
		{
			get
			{
				return this.birthday;
			}
			set
			{
				if (this.birthday != value)
				{
					this.birthday = value;
					OnPropertyChanged("Birthday");
					OnPropertyChanged("BirthdayDate");
				}
			}
		}
		private string birthday;

		public DateTime? BirthdayDate
		{
			get
			{
				try
				{
					return DateTime.ParseExact(this.Birthday, "yyyyMMdd", CultureInfo.InvariantCulture);
				}
				catch
				{
					return null;
				}
			}
			set
			{
				if (value == null)
				{
					this.Birthday = null;
				}
				else
				{
					this.Birthday = value.Value.ToString("yyyyMMdd");
				}
			}
		}

		public string Address
		{
			get
			{
				var helper = new ContentFormatHelper();
				var builder = new StringBuilder();

				if (!string.IsNullOrEmpty(this.POBox))
				{
					builder.Append(builder.Length > 0 ? ", " : "").Append(ContentFormatHelper.Escape(this.POBox));
				}
				if (!string.IsNullOrEmpty(this.RoomNumber))
				{
					builder.Append(builder.Length > 0 ? ", " : "").Append(ContentFormatHelper.Escape(this.RoomNumber));
				}
				if (!string.IsNullOrEmpty(this.HouseNumber))
				{
					builder.Append(builder.Length > 0 ? ", " : "").Append(ContentFormatHelper.Escape(this.HouseNumber));
				}
				if (!string.IsNullOrEmpty(this.City))
				{
					builder.Append(builder.Length > 0 ? ", " : "").Append(ContentFormatHelper.Escape(this.City));
				}
				if (!string.IsNullOrEmpty(this.Prefecture))
				{
					builder.Append(builder.Length > 0 ? ", " : "").Append(ContentFormatHelper.Escape(this.Prefecture));
				}
				if (!string.IsNullOrEmpty(this.ZipCode))
				{
					builder.Append(builder.Length > 0 ? ", " : "").Append(ContentFormatHelper.Escape(this.ZipCode));
				}
				if (!string.IsNullOrEmpty(this.Country))
				{
					builder.Append(builder.Length > 0 ? ", " : "").Append(ContentFormatHelper.Escape(this.Country));
				}

				string result = builder.ToString();
				return result;
			}
			set
			{
				this.POBox = null;
				this.RoomNumber = null;
				this.HouseNumber = null;
				this.City = null;
				this.Prefecture = null;
				this.ZipCode = null;
				this.Country = null;

				if (!string.IsNullOrEmpty(value))
				{
					string[] parts = ContentFormatHelper.Split(value);
					for (int i = 0; i < parts.Length; i++)
					{
						string part = ContentFormatHelper.Unescape(parts[i].Trim());
						switch (i)
						{
							case 0: this.POBox = part; break;
							case 1: this.RoomNumber = part; break;
							case 2: this.HouseNumber = part; break;
							case 3: this.City = part; break;
							case 4: this.Prefecture = part; break;
							case 5: this.ZipCode = part; break;
							case 6: this.Country = part; break;
						}
					}
				}
			}
		}

		public string POBox
		{
			get
			{
				return this.pobox;
			}
			set
			{
				if (this.pobox != value)
				{
					this.pobox = value;
					OnPropertyChanged("POBox");
				}
			}
		}
		private string pobox;

		public string RoomNumber
		{
			get
			{
				return this.roomNumber;
			}
			set
			{
				if (this.roomNumber != value)
				{
					this.roomNumber = value;
					OnPropertyChanged("RoomNumber");
				}
			}
		}
		private string roomNumber;

		public string HouseNumber
		{
			get
			{
				return this.houseNumber;
			}
			set
			{
				if (this.houseNumber != value)
				{
					this.houseNumber = value;
					OnPropertyChanged("HouseNumber");
				}
			}
		}
		private string houseNumber;

		public string City
		{
			get
			{
				return this.city;
			}
			set
			{
				if (this.city != value)
				{
					this.city = value;
					OnPropertyChanged("City");
				}
			}
		}
		private string city;

		public string ZipCode
		{
			get
			{
				return this.zipCode;
			}
			set
			{
				if (this.zipCode != value)
				{
					this.zipCode = value;
					OnPropertyChanged("ZipCode");
				}
			}
		}
		private string zipCode;

		public string Prefecture
		{
			get
			{
				return this.prefecture;
			}
			set
			{
				if (this.prefecture != value)
				{
					this.prefecture = value;
					OnPropertyChanged("Prefecture");
				}
			}
		}
		private string prefecture;

		public string Country
		{
			get
			{
				return this.country;
			}
			set
			{
				if (this.country != value)
				{
					this.country = value;
					OnPropertyChanged("Country");
				}
			}
		}
		private string country;

		public string WebsiteUrl
		{
			get
			{
				return this.websiteUrl;
			}
			set
			{
				if (this.websiteUrl != value)
				{
					this.websiteUrl = value;
					OnPropertyChanged("WebsiteUrl");
				}
			}
		}
		private string websiteUrl;

		public string NickName
		{
			get
			{
				return this.nickName;
			}
			set
			{
				if (this.nickName != value)
				{
					this.nickName = value;
					OnPropertyChanged("NickName");
				}
			}
		}
		private string nickName;

		public string FullName
		{
			get
			{
				return string.Format("{0} {1}", this.FirstName, this.LastName).Trim();
			}
		}

		public string FullAddress
		{
			get
			{
				var builder = new StringBuilder();

				if (!string.IsNullOrEmpty(this.Address) && this.Address != ";") //";" is work-around for empty field in web interface
				{
					//builder.Append(builder.Length > 0 ? ", " : "").Append(this.Address);
					return this.Address;
				}

				if (!string.IsNullOrEmpty(this.ZipCode) && this.ZipCode != ";")
				{
					builder.Append(builder.Length > 0 ? ", " : "").Append(this.ZipCode);
				}
				if (!string.IsNullOrEmpty(this.City) && this.City != ";")
				{
					builder.Append(builder.Length > 0 ? ", " : "").Append(this.City);
				}
				if (!string.IsNullOrEmpty(this.Country) && this.Country != ";")
				{
					builder.Append(builder.Length > 0 ? ", " : "").Append(this.Country);
				}

				string result = builder.ToString();
				return !string.IsNullOrEmpty(result) ? result : null;
			}
		}

		public string DisplayName
		{
			get
			{
				return this.FullName;
			}
		}
		#endregion

		#region Public methods
		public override void Parse(string content)
		{
			base.Parse(content);

			var helper = new ContentFormatHelper();
			string tag = helper.GetInlineTagName(content);
			if (tag != "MECARD")
			{
				throw new Exception("Missing 'MECARD' tag!");
			}

			this.PhoneNumberList = new List<string>();
			this.EmailAddressList = new List<string>();

			var dictionary = helper.ParseInline(content);
			foreach (var item in dictionary)
			{
				string key = item.Key;
				string value = item.Value;

				switch (key)
				{
					case "N":
						this.Name = value;
						break;

					case "SOUND":
						this.SoundName = value;
						break;

					case "NICKNAME":
						this.NickName = value;
						break;

					case "TEL":
						this.PhoneNumberList.Add(value);
						break;

					case "TEL-AV":
						this.VideoPhoneNumber = value;
						break;

					case "EMAIL":
						this.EmailAddressList.Add(value);
						break;

					case "BDAY":
						this.Birthday = value;
						break;

					case "ADR":
						this.Address = value;
						break;

					case "URL":
						this.WebsiteUrl = ContentFormatHelper.Unescape(value);
						break;

					case "NOTE":
						this.Note = ContentFormatHelper.DecodeNewLine(ContentFormatHelper.Unescape(value));
						break;
				}
			}
		}

		public override string Build()
		{
			var builder = new StringBuilder();
			builder.Append("MECARD:");
			builder.Append("N:").Append(this.Name).Append(";");

			foreach (var item in this.PhoneNumberList)
			{
				builder.Append("TEL:").Append(item).Append(";");
			}

			if (!string.IsNullOrEmpty(this.VideoPhoneNumber))
			{
				builder.Append("TEL-AV:").Append(this.VideoPhoneNumber).Append(";");
			}

			foreach (var item in this.EmailAddressList)
			{
				builder.Append("EMAIL:").Append(item).Append(";");
			}

			if (!string.IsNullOrEmpty(this.Note))
			{
				builder.Append("NOTE:").Append(ContentFormatHelper.EncodeNewLine(this.Note)).Append(";");
			}

			if (this.Birthday != null)
			{
				builder.Append("BDAY:").Append(this.Birthday).Append(";");
			}

			string address = this.Address;
			if (!string.IsNullOrEmpty(address))
			{
				builder.Append("ADR:").Append(address).Append(";");
			}

			if (!string.IsNullOrEmpty(this.WebsiteUrl))
			{
				builder.Append("URL:").Append(this.WebsiteUrl).Append(";");
			}

			if (!string.IsNullOrEmpty(this.NickName))
			{
				builder.Append("NICKNAME:").Append(this.NickName).Append(";");
			}

			return builder.Append(";").ToString();
		}

		public override string GetFriendlyName()
		{
			return this.FriendlyName;
		}
		#endregion
	}
}
